﻿using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Security;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Resources;

namespace Gmantis.Imaging
{
    public class GifImage
    {
        // Fields
        private bool _autoPlay;
        private Bitmap _bmp;
        private int _loopCount;
        private Uri _uriSource;

        // Events
        public event OpenReadCompletedEventHandler DownloadCompleted;

        public event EventHandler<DownloadProgressChangedEventArgs> DownloadProgress;

        public event EventHandler<ImageFailedExceptionEventArgs> ImageFailed;

        // Methods
        public GifImage()
        {
            this._bmp = new Bitmap();
            this._autoPlay = true;
        }

        public GifImage(Uri uriSource)
        {
            this._bmp = new Bitmap();
            this._autoPlay = true;
            this.UriSource = uriSource;
        }

        private object CreateBitmap(byte[] buffer)
        {
            try
            {
                Bitmap bitmap = new Bitmap(new MemoryStream(buffer));
                if (this._loopCount > 0)
                {
                    bitmap.Frames.LoopCount = this._loopCount;
                }
                return bitmap;
            }
            catch (Exception exception)
            {
                return exception;
            }
        }

        private void InitialBitmap(object result)
        {
            Bitmap source = result as Bitmap;
            Exception e = result as Exception;
            if (source != null)
            {
                this._bmp.BeginUpdate();
                this._bmp.Copy(source, false);
                this._bmp.Frames.Clear();
                this._bmp.Frames.AddRange(source.Frames);
                this._bmp.Frames.LoopCount = source.Frames.LoopCount;
                this._bmp.EndUpdate();
            }
            else if (e != null)
            {
                this.OnImageFailed(e);
            }
            if (!this.AutoPlay)
            {
                this.Stop();
            }
        }

        private void OnImageFailed(Exception e)
        {
            if (this.ImageFailed != null)
            {
                this.ImageFailed(this, new ImageFailedExceptionEventArgs(e));
            }
        }

        public static implicit operator ImageSource(GifImage gifImage)
        {
            return gifImage._bmp.ImageSource;
        }

        public void Pause()
        {
            if (this._bmp != null)
            {
                this._bmp.Frames.Pause();
            }
        }

        public void Play()
        {
            if (this._bmp != null)
            {
                if (this._bmp.Frames.CurrentState == ClockState.Stopped)
                {
                    this._bmp.Frames.BeginAnimation();
                }
                else
                {
                    this._bmp.Frames.Resume();
                }
            }
        }

        public void SetSource(Stream stream)
        {
            byte[] buffer = new byte[stream.Length];
            for (int i = (int)stream.Length; i > 0; i -= stream.Read(buffer, ((int)stream.Length) - i, i))
            {
            }
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (s, e) => e.Result = this.CreateBitmap(buffer);
            worker.RunWorkerCompleted += (s, e) => this.InitialBitmap(e.Result);
            worker.RunWorkerAsync();
        }

        private void SetUriSource(Uri uriSource)
        {
            if (uriSource != this.UriSource)
            {
                this._uriSource = uriSource;
                if (this._uriSource == null)
                {
                    this.SetSource(null);
                }
                else
                {
                    if (((this._uriSource.IsAbsoluteUri && (this._uriSource.Scheme == "file")) && ((Application.Current != null) && Application.Current.IsRunningOutOfBrowser)) && Application.Current.HasElevatedPermissions)
                    {
                        try
                        {
                            using (FileStream stream = File.OpenRead(this._uriSource.LocalPath))
                            {
                                this.SetSource(stream);
                            }
                        }
                        catch (SecurityException)
                        {
                        }
                    }
                    if (!this._uriSource.IsAbsoluteUri)
                    {
                        Uri uriResource = this._uriSource;
                        if (!Regex.Match(uriResource.ToString(), "^/.*?;component/").Success)
                        {
                            string str = Application.Current.GetType().Assembly.FullName.Split(new char[] { ',' })[0];
                            uriResource = new Uri(string.Format("/{0};component/{1}", str, this._uriSource.ToString()), UriKind.Relative);
                        }
                        StreamResourceInfo resourceStream = Application.GetResourceStream(uriResource);
                        if (resourceStream != null)
                        {
                            this.SetSource(resourceStream.Stream);
                            return;
                        }
                    }
                    try
                    {
                        WebClient client = new WebClient();
                        client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(this.wc_DownloadProgressChanged);
                        client.OpenReadCompleted += new OpenReadCompletedEventHandler(this.wc_OpenReadCompleted);
                        client.OpenReadAsync(this._uriSource);
                    }
                    catch (Exception exception)
                    {
                        this.OnImageFailed(exception);
                    }
                }
            }
        }

        public void Stop()
        {
            if (this._bmp != null)
            {
                this._bmp.Frames.Stop();
            }
        }

        private void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            if (this.DownloadProgress != null)
            {
                this.DownloadProgress(this, e);
            }
        }

        private void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                this.SetSource(e.Result);
                if (this.DownloadCompleted != null)
                {
                    this.DownloadCompleted(this, e);
                }
            }
            else
            {
                this.OnImageFailed(e.Error);
            }
        }

        // Properties
        public bool AutoPlay
        {
            get
            {
                return this._autoPlay;
            }
            set
            {
                this._autoPlay = value;
            }
        }

        public int FrameCount
        {
            get
            {
                if (this._bmp == null)
                {
                    return 0;
                }
                return this._bmp.Frames.Count;
            }
        }

        public int LoopCount
        {
            get
            {
                if (this._loopCount > 0)
                {
                    return this._loopCount;
                }
                if (this._bmp == null)
                {
                    return 0;
                }
                return this._bmp.Frames.LoopCount;
            }
            set
            {
                this._loopCount = value;
                if (this._bmp != null)
                {
                    this._bmp.Frames.LoopCount = value;
                }
            }
        }

        public Uri UriSource
        {
            get
            {
                return this._uriSource;
            }
            set
            {
                this.SetUriSource(value);
            }
        }
    }
}
